﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using CaptchaLearning.Infrastructure.Model;

namespace CaptchaLearning.Infrastructure.Algorithm
{
    public class Evaluate
    {
        public double[,] Forward(Character character, ObservableCollection<ImageColumn> observations)
        {
            List<List<double>> A = character.TransitionMatrix.Probability;
            List<List<double>> B = character.EmissionMatrix.Probability;
            ObservableCollection<double> Pi = character.Pi;

            int T = observations.Count;
            //int T = K + 2;
            int States = character.States.Count;

            double[,] a = new double[States, T];
            // initialize
            for (int i=0; i<States; i++)
                a[i, 0] = Pi[i];
            // calculate at each time
            for (int t = 1; t < T; t++)
            {
                // at each states
                for (int s = 0; s < States; s++)
                {
                    a[s, t] = 0;
                    for (int s1 = 0; s1 < States; s1++)
                    {
                        int h = t;
                        for (int k=0; k<T; k++)
                            if (observations[t].Equals(character.Observations[k]))
                            {
                                h = k;
                                break;
                            }
                        a[s, t] += a[s1, t - 1] * A[s1][s] * B[s][h];
                    }
                }
            }
            // return the matrix
            return a;
        }

        public double[,] Backward(Character character, ObservableCollection<ImageColumn> observations)
        {
            List<List<double>> A = character.TransitionMatrix.Probability;
            List<List<double>> B = character.EmissionMatrix.Probability;

            int T = observations.Count;
            //int T = K + 2;
            int States = character.States.Count;

            double[,] b = new double[States, T];
            // initialize
            for (int s = 0; s < States; s++)
                b[s, T-1] = 1;
            // calculate at each time
            for (int t = T-2; t >= 0; t--)
            {
                // at each real states (exclude start and end states)
                for (int s = 0; s < States; s++)
                {
                    b[s, t] = 0;
                    int h = t+1;
                    for (int k = 0; k < T; k++)
                        if (observations[t+1].Equals(character.Observations[k]))
                        {
                            h = k;
                            break;
                        }
                    for (int s1 = 0; s1 < States; s1++)
                        b[s, t] += b[s1, t + 1] * A[s][s1] * B[s1][h];
                }
            }
            // return the matrix
            return b;
        }

        /// <summary>
        /// Hàm này để tính toán xác suất sinh ra chuỗi quan sát dựa trên mô hình hmm cho trước
        /// </summary>
        public double ComputeLikelihood(Character character, ObservableCollection<ImageColumn> observations)
        {
            // number of states
            int States = character.States.Count;
            // number of observations
            int K = character.Observations.Count;
            // forward matrix
            double[,] alpha = this.Forward(character, observations);

            double P = 0; // P(O|Lamda) = sum of the probability in the final column of observations
            for (int p = 0; p < States; p++)
                P += alpha[p, K - 1];
            return P;
        }
    }
}
